package br.inf.ufrgs.usixml4desktop.logicconector.java;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import br.inf.ufrgs.usixml4desktop.exceptions.MethodCallException;
import br.inf.ufrgs.usixml4desktop.exceptions.MethodConfigurationException;

public class MethodDefinition {
	
	private String className;
	private String methodName;
	
	private Map<Integer, ParameterType> parametersDefinition = new HashMap<Integer, ParameterType>();
	private ParameterType returnType;
	
	private Map<Integer,Integer> integerParameter = new HashMap<Integer, Integer>();
	private Map<Integer,Double> doubleParameter = new HashMap<Integer, Double>();
	private Map<Integer,String> stringParameter = new HashMap<Integer, String>();
	
	private String returnString = null;
	private Integer returnInteger = null;
	private Double returnDouble = null;
	
	public MethodDefinition() {
	}
	
	public void setClassName(String name){
		this.className = name;
	}
	
	public void setMethodName(String name){
		this.methodName = name;
	}
	
	public void setMethodReturnType(ParameterType returnType){
		this.returnType = returnType;
	}
	
	public void addParameter(String parameter, int position){
		this.parametersDefinition.put(position, ParameterType.PARAMETER_STRING);
		this.stringParameter.put(position, parameter);
	}
	
	public void addParameter(Integer parameter, int position){
		this.parametersDefinition.put(position, ParameterType.PARAMETER_INTEGER);
		this.integerParameter.put(position, parameter);
	}
	
	public void addParameter(Double parameter, int position){
		this.parametersDefinition.put(position, ParameterType.PARAMETER_DOUBLE);
		this.doubleParameter.put(position, parameter);
	}
	
	public void callMethod()
		throws MethodCallException{
		
		try {
			if (this.className == null || this.methodName == null){
				throw new MethodCallException("Class or method name not configured");
			}else{
				int numParameters = this.parametersDefinition.size();
				final Class[] parameterTypeArray = new Class[numParameters];
				final Object[] parameterArray = new Object[numParameters];
				
				for (int i = 0; i < numParameters; i++){
					ParameterType parType = this.parametersDefinition.get(i);
					
					switch (parType) {
					case PARAMETER_DOUBLE:
						parameterTypeArray[i] = double.class;
						parameterArray[i] = this.doubleParameter.get(i);
						break;
					case PARAMETER_INTEGER:
						parameterTypeArray[i] = int.class;
						parameterArray[i] = this.integerParameter.get(i);
						break;
					case PARAMETER_STRING:
						parameterTypeArray[i] = String.class;
						parameterArray[i] = this.stringParameter.get(i);
						break;
					default:
						throw new MethodCallException("Invalid parameter type");
					}
				}
				
				
				Object obj = DomainModel.getInstance().getDomainModelObject(this.className);
				Method method = obj.getClass().getMethod(this.methodName, parameterTypeArray);
								
				Object returnValue = method.invoke(obj, parameterArray);
				
				switch (this.returnType) {
				case PARAMETER_DOUBLE:
					this.returnDouble = (Double)returnValue;
					break;
				case PARAMETER_INTEGER:
					this.returnInteger = (Integer)returnValue;
					break;
				case PARAMETER_STRING:
					this.returnString = (String)returnValue;
					break;
				default:
					throw new MethodCallException("Invalid return type");
				}
				
			}
		
		
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		
		
		
	}
	
	public Integer getIntegerReturnValue()
		throws MethodConfigurationException{
		
		if (this.returnType.equals(ParameterType.PARAMETER_INTEGER)){
			return this.returnInteger;
		}else{
			throw new MethodConfigurationException("Invalid method return type");
		}
	}
	
	public String getStringReturnValue()
		throws MethodConfigurationException{
		
		if (this.returnType.equals(ParameterType.PARAMETER_STRING)){
			return this.returnString;
		}else{
			throw new MethodConfigurationException("Invalid method return type");
		}
	}
	
	public Double getDoubleReturnValue()
		throws MethodConfigurationException{

		if (this.returnType.equals(ParameterType.PARAMETER_DOUBLE)){
			return this.returnDouble;
		}else{
			throw new MethodConfigurationException("Invalid method return type");
		}
	}

}
